home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 7 / BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso / Files / Prog / M / mini.cpt / mini / xmodem.c < prev    next >
Text File  |  1987-07-04  |  33KB  |  1,106 lines

  1. /* This file contains the implementation of the xmodem protocol
  2.    and its varients "Text" and  "MacBinary"
  3.    
  4. Author:    Jerry LeVan
  5.            325 Boone Trail
  6.            Richmond Ky 40475
  7.            New File: May 27,1987
  8.    
  9. */
  10. #include <types.h>
  11. #include <quickdraw.h>
  12. #include <files.h>
  13. #include <strings.h>
  14. #include <setJmp.h>
  15. #include <dialogs.h>
  16. #include <packages.h>
  17. #include <events.h>
  18. #include <devices.h>
  19. #include <errors.h>
  20.  
  21.  
  22. #define FLInit 0x100    /* the init flag */
  23.  
  24. #define NUL  0
  25. #define SOH  1
  26. #define EOT  4
  27. #define ACK  6
  28. #define NAK 21
  29. #define CAN 24
  30. #define CTLZ 26
  31. #define CRCCHAR  67
  32.  
  33. #define CR 13
  34. #define LF 10
  35.  
  36. #define GEN1x5x12x16 0x1021
  37.  
  38. #define ERRLIM 10
  39. #define NAKLIM 10
  40. #define EOTLIM 10
  41. #define ACKLIMTIM 10
  42. #define NAKTIM 12
  43.  
  44. /* the following five items track the users xmodem preferences */
  45. /* they are set in DoMenu in file term.c */
  46.  
  47.  char MacBinary = 1;    /* true means try macbinary else standard xmodem */
  48.  char TextXMode = 0;    /* true means append Lf after Cr on upload */
  49.                          /* and strip Lf after Cr on download */
  50.                         
  51.  char fastDL = 0;        /* true implies a fast ack */
  52.  char forceCheckSum = 0 ;    /* true means disable CRC */
  53.  long sectorSize = 128;
  54. /******************************************************************/
  55. /* these variables are defined in term.c */                            
  56. extern short sOut;            /* output modem refnum */
  57. extern short sIn;            /* input modem refnum */
  58. extern OSType creator;        /* text file creator type */
  59. extern WindowPtr myWindow;    /* tty window pointer */
  60.  
  61. /* this variable is defined in clock.c */
  62. extern Boolean showClock;
  63.  
  64. /* the following items are used by the "in progress" dialog */
  65.   DialogPtr dlog;    /* will point to the progress dialog */
  66.   Handle statusHandle;    /* for quick access */
  67.   
  68. #define PROGRESS_ID 131
  69. #define TRANS_ITEM 13
  70. #define PROTOCOL_ITEM 14
  71. #define ERROR_ITEM 15
  72. #define FILE_ITEM 12
  73. #define BLOCK_ITEM 6
  74. #define OF_ITEM 7
  75. #define TOTAL_ITEM 8
  76. #define STATUS_ITEM 10
  77.  
  78.  
  79.   char  logFileName[64];    /* received file name */
  80.   short logFileRefNum;        /* Vol/Dir refnum */
  81.   short logFile;            /* channel for FSRead */ 
  82.   
  83.   char thefilename[64];        /* name of file being sent */
  84.   short thefileRefNum;        /* Vol/Dir refnum */
  85.   short thefile;            /* channel for FSWrite */
  86.   
  87.   long gTotal;                /* total number of blocks to transfer */
  88.  
  89.   /* a table for fast computations of crc's (thanks to Bela Lubkin) */
  90.   unsigned short crc_table[256];
  91.   
  92.   /* a buffer for setjmp/longjmp */
  93.   jmp_buf env;
  94.   
  95. #define __SEG__ Xmodem
  96.  
  97. /*********************************code*********************************/
  98. /* table lookup crc calculations */
  99.  
  100. unsigned short compute_crc(crc,bufptr,len)
  101. unsigned short crc;
  102. char *bufptr;
  103. short len;
  104. {
  105.         int     i;
  106.         while (len--) {
  107.                 crc ^= (unsigned short)(*bufptr++) << 8;
  108.                 for (i = 0; i < 8; ++i) {
  109.                         if (crc & 0x8000)
  110.                                 crc = (crc << 1) ^ 0x1021;
  111.                         else
  112.                                 crc <<= 1;
  113.                 }
  114.         }
  115.         return(crc);
  116. }
  117. setup_crc_tables()
  118. {
  119.   int count;
  120.   char zero;
  121.   
  122.   zero = 0;
  123.   for (count=0; count<256; count++)
  124.     crc_table[count]=compute_crc(count<<8,&zero,1);
  125. }
  126.  
  127. unsigned short table_driven_crc(crc,bufptr,len)
  128. register unsigned short crc;
  129. register unsigned char *bufptr;
  130. register short len;
  131. {
  132.   while (len--)
  133.     /* on some C systems the index is sign extended */
  134.     crc=crc_table[crc>>8^(*bufptr++)]^crc<<8;
  135.   return(crc);
  136. }
  137.  
  138. unsigned char CheckSum(buff,size)
  139.  char * buff;
  140.  short size;
  141.  { unsigned char sum;
  142.    short i;
  143.    sum = 0;
  144.    for(i=0;i<size;i++)
  145.      sum = sum + buff[i];
  146.    return sum;
  147.   }
  148. /**************************** Utilities *********************************/  
  149. static Boolean ChAvail( chan) /* return true if char avail in serial port */
  150.  short chan;
  151.  { OSErr err;
  152.    long cnt;
  153.    if(err = SerGetBuf(chan,&cnt))return false;
  154.    if (cnt) return true;
  155.    return false;
  156.   }
  157.  
  158. /* This procedure should be changed to do the timing in a processor
  159.    independent way eg a rom call
  160. */
  161. short TimedRead(chan,buff,count,limit)
  162.       short chan;
  163.       char buff[];
  164.       short count;
  165.       long limit;
  166.       
  167.       { long theLimit;
  168.         short i;
  169.         long cnt;
  170.     
  171.     i = 0;
  172.     theLimit = *(long *) 0x20c + limit; /* time stored here */
  173.     
  174.     while (theLimit >= *(long*)0x20c )
  175.      { if(ChAvail(chan))
  176.          { cnt = 1; FSRead(chan,&cnt,&buff[i]); i++;
  177.            if ( i == count ) return 1; /* success */
  178.           }
  179.       }
  180.      return 0;  /* failure */
  181.        }
  182.        
  183. static void OutCh(chan,ch)
  184.  short chan;    /* output refnum */
  185.  char ch;        /* the character to write */
  186.  { long cnt;
  187.    cnt = 1;
  188.    FSWrite(chan,&cnt,&ch);  /* ignore serial port errror for now */
  189.   }
  190.  
  191. void Cancel(chan,errno)
  192.      short chan;
  193.      short errno;
  194.      { char c;
  195.        void OutCh();
  196.      
  197.        while( TimedRead(chan,&c,1,1)); /* eat garbage */
  198.        
  199.        OutCh(sOut,CAN);     /* notify the other end */
  200.        
  201.        while(TimedRead(chan,&c,1,1));   /* eat more trash */
  202.        
  203.        longjmp(env,errno);
  204.       }
  205.  
  206.   
  207. static void OutBlock(refnum,buff,size)
  208.   short refnum; /* channel to use */
  209.   char * buff;    /* address of buffer to write */
  210.   long size;    /* the number of chars to write */
  211.   { OSErr err;
  212.     long cnt;    /* kludge so write won't change sector size */
  213.     cnt = size;
  214.     if(err = FSWrite(refnum,&cnt,buff)){
  215.       ErrorMessage("Fatal Error Writing File",err);
  216.       Cancel(sIn,6);
  217.      }
  218.    }
  219.    
  220.   
  221. static void Message(theMessage)
  222.  char * theMessage;
  223.  { 
  224.     SetIText(statusHandle,theMessage);
  225.   };
  226.   
  227. static Boolean FindFile(name,vRefNum)
  228.   char * name;        /* name we are looking for */
  229.   short vRefNum;    /* dir/vol refnum */
  230.   { FInfo info;
  231.     OSErr err;
  232.     if(err = GetFInfo(name,vRefNum,&info))
  233.       return false;
  234.     else return true;
  235.    }
  236.    
  237. void SetFileInformation(buff)
  238.   char buff[];
  239.   { char localname[64];  /* prefered macbinary name */
  240.     IOParam pb;         /* io parameter block for rename */
  241.     FileParam fileBlock; /* parameter block for setting finder info */
  242.     char *ptr;
  243.     char temp[64];       /* a string buffer */
  244.     long err;
  245.     short type;
  246.     Handle item;
  247.     Rect box;
  248.     
  249.     strcpy(temp,"Copy of ");
  250.     strcpy(localname,p2cstr(&buff[1]));  /* make c string */
  251.     
  252.     if( FindFile(localname,logFileRefNum)) /* whoops file already exists */
  253.        { strcpy(localname,strcat(temp,localname)); /* extend name */
  254.          if(FindFile(localname,logFileRefNum)) /* give up */
  255.          FSDelete(localname,logFileRefNum);
  256.     }
  257.     /* here localname is an OK name for a file (if not too long) */
  258.     
  259.     /* rename the log file to reflect the macbinary name */
  260.     err=Rename(logFileName,logFileRefNum,localname);
  261.     if(err)ErrorMessage("Error During Rename",err);
  262.     
  263.     /* Put the new name up on the board */
  264.     GetDItem(dlog,FILE_ITEM,&type,&item,&box);
  265.     SetIText(item,localname);
  266.     
  267.     
  268.     /* record the new name */
  269.     strcpy(logFileName,localname);
  270.     
  271.     /* set default volume */
  272.       memset(&pb,0,sizeof(IOParam));
  273.       pb.ioRefNum = logFile;
  274.       if(err=PBFlushFile(&pb,0))
  275.         ErrorMessage("Error Flushing File",err);
  276.  
  277.     
  278.     /* reset the finder info  */
  279.     
  280.     memset(&fileBlock,0,sizeof(FileParam));
  281.     fileBlock.ioNamePtr = c2pstr(localname); 
  282.     fileBlock.ioVRefNum = logFileRefNum;
  283.     err = PBGetFInfo(&fileBlock,0);
  284.     if(err)ErrorMessage("Error in GetFInfo",err);
  285.     
  286.     memcpy(&fileBlock.ioFlFndrInfo,&buff[65],10);
  287.     memcpy(&fileBlock.ioFlCrDat,&buff[91],8);
  288.     
  289.     /* so the icons won't stack clear the inited bit */
  290.     fileBlock.ioFlFndrInfo.fdFlags &= ~FLInit ;
  291.     
  292.     err = PBSetFInfo(&fileBlock,0);
  293.     if(err)ErrorMessage("Error in SetFileInfo",err);
  294.     
  295.   }
  296.     
  297. void ProcessBlock(buff,blkno)
  298.   char buff[];
  299.   long blkno;
  300.   { static long dblks;       /* the number of data blocks expected */
  301.     static long rblks;       /* the number of resource blocks expected */
  302.     static long lastdbytes;  /* number of bytes in last data block */
  303.     static long lastrbytes;  /* number of bytes in last resource block */
  304.     static long  binary;     /* true if we discover macbinary format */
  305.     static long dforklen;    /* length of datafork in bytes */
  306.     static long rforklen;    /* length of resource fork in bytes */
  307.     char temp[10];
  308.     short i;
  309.     Handle item;
  310.     short type;
  311.     Rect box;
  312.     
  313.     if(blkno==1)  /* first sector tells all */
  314.      if(buff[0] || buff[74] || buff[82]) /* is this a macbinary file? */
  315.        { binary = 0; /* nope set flag to no binary */
  316.          OutBlock(logFile,buff,sectorSize);
  317.           return;
  318.         }
  319.       else /* this is a macbinary format file */
  320.         { GetDItem(dlog,PROTOCOL_ITEM,&type,&item,&box);
  321.       SetIText(item,"MacBinary");
  322.           GetDItem(dlog,OF_ITEM,&type,&item,&box);
  323.       SetIText(item,"Of");
  324.       Message("Changing Log File Name");
  325.       SetFileInformation(buff); /* set finder info & change name */
  326.       memcpy(&dforklen,&buff[83],4);
  327.       dblks = (dforklen+sectorSize-1)/sectorSize;
  328.       lastdbytes = dforklen % sectorSize;
  329.       if(!lastdbytes)lastdbytes = sectorSize;
  330.       if(!dforklen)
  331.         {FSClose(logFile); /* close data fork and open resource fork */
  332.         logFile = 0;
  333.          OpenRF(logFileName,logFileRefNum,&logFile);
  334.          }
  335.       memcpy(&rforklen,&buff[87],4);
  336.       rblks = (rforklen +sectorSize-1)/sectorSize;
  337.       gTotal = rblks+dblks+1;
  338.       GetDItem(dlog,TOTAL_ITEM,&type,&item,&box);
  339.       NumToString(gTotal,temp);
  340.       SetIText(item,temp);
  341.       
  342.       lastrbytes = rforklen % sectorSize;
  343.       if(!lastrbytes)lastrbytes = sectorSize;
  344.       
  345.       binary = 1;
  346.       return;
  347.      }
  348.      
  349.     if (!binary) { OutBlock(logFile,buff,sectorSize);return;}
  350.     
  351.     if (blkno == dblks+1) /* last block of data fork */
  352.        { OutBlock(logFile,buff,lastdbytes);
  353.          FSClose(logFile); /* close data fork and open resource fork */
  354.         logFile = 0;
  355.         OpenRF(logFileName,logFileRefNum,&logFile);
  356.         return;
  357.     }
  358.     
  359.     if (blkno == dblks+rblks+1) /* last resource block */
  360.        { OutBlock(logFile,buff,lastrbytes); return;}
  361.        
  362.      OutBlock(logFile,buff,sectorSize); /* of the mill block */
  363.      
  364.   } /* ProcessBlock */
  365.     
  366. /********************************************************************/
  367.  
  368. short ReceiveFile()
  369.    {  
  370.       IOParam pblock ;
  371.       Point loc;
  372.       SFReply reply;
  373.       
  374.       loc.v = 80;
  375.       loc.h = 100;
  376.     
  377.     
  378.     SFPutFile(&loc, "Log File", "LogFile", 0, &reply);
  379.     
  380.     if( reply.good )
  381.      {
  382.      /* if the reply was good then we must set the default volume */
  383.    
  384.       memset(&pblock,0,sizeof(IOParam));
  385.       pblock.ioVRefNum = reply.vRefNum ;
  386.       logFileRefNum = reply.vRefNum;
  387.       PBSetVol(&pblock,0);
  388.       
  389.       p2cstr(&reply.fName);  /* convert to c format */
  390.       
  391.       strcpy(logFileName,&reply.fName); /* save copy for delete */
  392.       FSDelete(logFileName,logFileRefNum);
  393.    
  394.      /* Open the target file */
  395.    
  396.      Create(logFileName,logFileRefNum,creator,'TEXT');
  397.      FSOpen(logFileName,logFileRefNum,&logFile);     
  398.      return 1;
  399.        
  400.       } /* good reply */
  401.      return 0;
  402.     }
  403.     
  404.   
  405. /********************************************************************/
  406.  
  407. short Sendfile()
  408.   {
  409.     Point loc;
  410.     IOParam pblock ;
  411.     SFTypeList typeList;
  412.     SFReply reply;
  413.     short ntypes;
  414.     OSErr err;
  415.     
  416.     loc.v = 80;
  417.     loc.h = 100;
  418.     ntypes = (MacBinary)? -1:1; /* allow all files if MacBinary true */
  419.     
  420.     typeList[0] = 'TEXT';
  421.     
  422.     SFGetFile(&loc, 0, 0, ntypes, typeList, 0, &reply);
  423.     
  424.     if( reply.good )
  425.     {
  426.      /* if the reply was good then set the default volume */
  427.    
  428.       memset(&pblock,0,sizeof(IOParam));
  429.       pblock.ioVRefNum = reply.vRefNum ;
  430.       PBSetVol(&pblock,0);
  431.       
  432.       p2cstr(&reply.fName);  /* convert to c format */
  433.       
  434.       thefileRefNum = reply.vRefNum;
  435.       strcpy(thefilename,&reply.fName); /* save copy for open */
  436.    
  437.      /* Open the target file */
  438.       if(err = FSOpen(thefilename,thefileRefNum,&thefile)){
  439.         ErrorMessage("Can't Open File",err);
  440.         return 0;
  441.        }
  442.       /* compute the size of the transfer in blocks...if macbinary
  443.          this number will be changed
  444.       */
  445.       GetEOF(thefile,&gTotal);
  446.       gTotal = ( gTotal+sectorSize-1)/sectorSize;
  447.             
  448.        
  449.       return 1;
  450.      } /* good reply */
  451.       return 0 ;
  452.    } /* GetAfile */
  453.  /********************************************************************/  
  454. void BadData(err,crc)
  455.   short *err;
  456.   short crc;    /* true if attempting to tell sender to use CRC */
  457.   { char trash;
  458.     
  459.     /* eat garbage until line is quiet */
  460.     while(TimedRead(sIn,&trash,1,1));
  461.     
  462.     if (!crc) OutCh(sOut,NAK);  /* signal error */
  463.     else OutCh(sOut,CRCCHAR);   /* request CRC transmission */
  464.     
  465.     if((*err)++ >= ERRLIM) longjmp(env,3); /* give up*/
  466.    }
  467.  /********************************************************************/ 
  468.  
  469. short BlockTimedRead(buff,count,limit)
  470.  char *buff;  /* data goes here */
  471.  short count; /* this many characters */
  472.  long limit;  /* timeout value in seconds */
  473.  
  474.  { IOParam rBlock ;     /* block for describing read */
  475.    CntrlParam kBlock ;  /* block for the Killio call */
  476.    long theLimit;    /* read must complete by this time */
  477.    EventRecord event;   /* check for mouse down */
  478.    short giveup;    /* set to true if mousedown detected */
  479.     
  480.    /* set up for asynch read */
  481.    rBlock.ioCompletion = 0 ;   /* no completion routine */
  482.    rBlock.ioVRefNum = 0;
  483.    rBlock.ioRefNum = sIn; /* select read port */
  484.    rBlock.ioBuffer = buff;     /* where to read to */
  485.    rBlock.ioReqCount = count;    /* byte count to read */
  486.    rBlock.ioPosMode = 0;
  487.    
  488.    PBRead(&rBlock,1);     /* post asynch read */
  489.    
  490.    /* wait for the read to complete or for a timeout */
  491.    theLimit = *( long *)0x20c + limit ; /* compute due time */
  492.    
  493.    /* here we can update the clock and check for a user cancel */
  494.    
  495.    giveup = 0;
  496.    if(GetNextEvent(mDownMask,&event)) giveup =1; /* give up */
  497.    if(showClock)
  498.      if (GetNextEvent(app2Mask,&event))showTime(); /* adjust clock */
  499.  
  500.  
  501.    /* wait */
  502.    while((rBlock.ioResult == 1) && (theLimit >= *( long *)0x20c));
  503.    
  504.    
  505.    /* how have we escaped? */
  506.    if(rBlock.ioResult == 0) return 1;  /* read has completed */
  507.    
  508.    /* here if timeout or error during read...Killio to cleanup */
  509.    kBlock.ioCompletion = 0;
  510.    kBlock.ioCRefNum = sIn;
  511.    
  512.    PBKillIO(&kBlock,0); /* synch  */
  513.    
  514.    /* if we detected a mousedown while waiting then abort */
  515.    if (giveup) longjmp(env,4);
  516.  
  517.    
  518.    return 0;   /* signify error */
  519.   } 
  520.  /********************************************************************/  
  521.    
  522. void Xreceive()
  523.   { char buff[1024+10];
  524.     char c;
  525.     char blk,notBlk;           /* block number and its complement */
  526.     short errCount;            /* cumulative errors for one block */
  527.     char blockNumber;          /* expected blocknumber */
  528.     static long total;        /* total blocks received */
  529.     char RcheckSum;            /* received checksum */
  530.     char checkSum;            /* calculated checksum */
  531.     short error;            /* error code for catchsignal */
  532.     short i;                /* for loop index */
  533.     short crcMode;            /* true if expecting CRC */
  534.     short firstSOH;            /* true until we receive first SOH */
  535.     short readSize;            /* Size of packet to get */
  536.     IOParam pblock;            /* parameter block for vol flush */
  537.     EventRecord event;        /* catches mousedown to cancel */
  538.     Rect box;                /* temp for getditem */
  539.     short type;                /* temp storage */
  540.     Handle item;            /* temp handle storage */
  541.     Handle blockNumH;        /* handle to item to show current block */
  542.     char temp[10];            /* storage for converting integers */
  543.     static WindowPtr old;    /* previous graphport */
  544.     static long stime,ftime;/* start and finish time in ticks */
  545.     unsigned short crcVal;    /* for the crc */
  546.     
  547.  
  548.     /* ask the user to select a file, if he quits so do we */     
  549.     if(!ReceiveFile()){OutCh(sOut,CAN);OutCh(sOut,CAN);return ; }
  550.     
  551.             
  552.     /* bring the progress dialog */
  553.      GetPort(&old);        /* remember the port */
  554.      HideWindow(myWindow);    /* hide the tty window */
  555.      
  556.      /* create the dialogue */
  557.      dlog = (DialogPtr)GetNewDialog(PROGRESS_ID ,0,(WindowPtr)-1);
  558.      
  559.      DrawDialog(dlog);
  560.      
  561.      /* get a handle to the status field for ease of access */
  562.      
  563.      GetDItem(dlog,STATUS_ITEM,&type,&statusHandle,&box);
  564.      GetDItem(dlog,BLOCK_ITEM,&type,&blockNumH,&box);
  565.      
  566.      /* set some fields */
  567.      GetDItem(dlog,TRANS_ITEM,&type,&item,&box);
  568.      SetIText(item,"Receiving");
  569.      GetDItem(dlog,PROTOCOL_ITEM,&type,&item,&box);
  570.      if(TextXMode)SetIText(item,"Text Xmodem");
  571.         else SetIText(item,"Xmodem");
  572.      GetDItem(dlog,ERROR_ITEM,&type,&item,&box);
  573.      if(fastDL)SetIText(item,"Checksum,Abort On Error");
  574.      else SetIText(item,"Eight Bit Checksum With ARQ");
  575.      GetDItem(dlog,FILE_ITEM,&type,&item,&box);
  576.      SetIText(item,logFileName);
  577.      Message("Waiting For Handshake");
  578.      
  579.     /* ready to go but first let's establish an error handler */
  580.     if( error = setjmp(env))
  581.        { DisposDialog(dlog); ShowWindow(myWindow);SetPort(old);
  582.          switch(error) 
  583.           { case 1:drawCString("\r*** Transmitter Cancel***\012");break;
  584.             case 2: ftime = TickCount(); /* normal exit */
  585.                 FSClose(logFile);logFile = 0;
  586.                 drawCString("\r***Transfer Completed***\r\012");
  587.                 NumToString(sectorSize*(total-1),temp);
  588.                 drawCString(temp);
  589.                 drawCString(" user bytes received.");
  590.                 drawCString(" Effective Transfer Rate = ");
  591.                   NumToString((sectorSize*(total-1)*60)/(ftime-stime),temp);
  592.                 drawCString(temp);
  593.                 drawCString(" bytes/second.\r\012");
  594.                          
  595.                 OutCh(sOut,ACK);OutCh(sOut,ACK);
  596.             
  597.             /* make sure we have the file if we later crash */
  598.                 memset(&pblock,0,sizeof(IOParam));
  599.                 pblock.ioVRefNum = logFileRefNum ;
  600.                 PBFlushVol(&pblock,0);
  601.             
  602.                 SysBeep(7);SysBeep(7);
  603.                 return;
  604.         case 3:    NumToString(total,temp);
  605.                 buff[0]=0;
  606.                 strcpy(buff,"Too Many Errors For Block ");
  607.                 strcat(buff,temp);
  608.                    drawCString(buff);
  609.                    OutCh(sOut,CAN);
  610.                break;
  611.         case 4:drawCString("\rReceiver Cancelled Transfer");
  612.                OutCh(sOut,CAN);
  613.                break;
  614.         case 6: drawCString("\r***file system error***");break;
  615.         default:drawCString("\r***Unknown Signal***");break;
  616.        }
  617.       FSClose(logFile);logFile = 0;
  618.       FSDelete(logFileName,logFileRefNum);
  619.       SysBeep(7);SysBeep(7);
  620.       return; 
  621.      } /* end of handler */
  622.             
  623.     /* user selected a file so lets crank up the transmitter */
  624.     blockNumber = 1; total = 1;
  625.     errCount = 0; firstSOH = 1;
  626.     if(forceCheckSum)crcMode = 0;else crcMode =1;
  627.     BadData(&errCount,crcMode);   /* this will send a NAK/C to crank up the other side */
  628.     
  629.     while(true)
  630.      {
  631.       if(!TimedRead(sIn,&c,1,NAKTIM)) /* wait for a charcter */
  632.         { Message("Timeout waiting for SOH");
  633.       BadData(&errCount,crcMode);
  634.       if(firstSOH && (errCount>=3)) crcMode = 0;
  635.       continue;}
  636.       
  637.       /* examine the captured character */
  638.       switch(c)
  639.        { case NUL: continue;    /* skip nulls */
  640.          case CAN: longjmp(env,1);   /* transmitter gave up */
  641.           case EOT: longjmp(env,2);   /* we are done */
  642.           case SOH: break;         /* start of block let's go! */
  643.           default : Message("Bad Character, Expected SOH");
  644.             BadData(&errCount,crcMode);
  645.            /* give the transmitter 3 shots at recognizing CRC */
  646.            if ((errCount >= 3) && firstSOH) crcMode = 0;
  647.                continue;
  648.     }
  649.     
  650.       /* pick up the rest of the packet */
  651.       if (firstSOH && crcMode) {
  652.        GetDItem(dlog,ERROR_ITEM,&type,&item,&box);
  653.        if(fastDL)SetIText(item,"CRC, Abort on Error");
  654.        else SetIText(item,"CCITT-CRC With ARQ");
  655.        }
  656.        
  657.       if(firstSOH) stime = TickCount();   /* sample the clock */ 
  658.       
  659.       firstSOH = 0;  /* pay no attention to recognizing mode */
  660.       
  661.       if (crcMode)  readSize = sectorSize + 4;
  662.       else readSize = sectorSize + 3;
  663.       
  664.  
  665.       if(!BlockTimedRead(buff,readSize,NAKTIM))
  666.             { Message("Timeout waiting for data");
  667.           BadData(&errCount,crcMode); continue ;}
  668.           
  669.     /* if the user selected fast download then send back an ACK */
  670.     if(fastDL)OutCh(sOut,ACK); /* acknowlege the block */
  671.       
  672.     /* check for a scrambled block */
  673.     if ( buff[0] != ~buff[1])
  674.       if(fastDL) longjmp(env,4); /* give up on this option */
  675.       else
  676.          { Message("Error,scrambled block number");
  677.            BadData(&errCount,crcMode); continue;}
  678.                   
  679.     /* check the checksum */
  680.     if (crcMode)
  681.       { 
  682.         crcVal = table_driven_crc(0,&buff[2],sectorSize+2,crc_table);
  683.         if ((crcVal & 0xFFFF) != 0)
  684.          if(fastDL) longjmp(env,4);
  685.           else
  686.            { Message("Bad CRC");
  687.              BadData(&errCount,crcMode);
  688.          continue;
  689.         }
  690.        }
  691.     else
  692.     { 
  693.       if ( buff[sectorSize+2] != CheckSum(&buff[2],sectorSize))
  694.        if(fastDL) longjmp(env,4);
  695.        else
  696.          { Message("Bad Checksum");
  697.            BadData(&errCount,crcMode); continue;}
  698.      }
  699.       
  700.      /* if this block is the previous block then ACK but don't write */
  701.      if ( buff[0] == blockNumber-1)
  702.       if(fastDL)continue;
  703.       else
  704.          { OutCh(sOut,ACK); continue;}
  705.          
  706.      /* if this block is not the expected block then bad block */
  707.      if ( buff[0] != blockNumber)
  708.       if(fastDL) longjmp(env,4);
  709.       else
  710.           { Message("Out of sequence blocknumber");
  711.             BadData(&errCount,crcMode); continue;}
  712.           
  713.      /* if we get here we have the validated expected block */
  714.      
  715.      
  716.      if(!fastDL)OutCh(sOut,ACK); /* acknowlege the block */
  717.      
  718.      if (MacBinary) ProcessBlock(&buff[2],total);
  719.      else
  720.      if(TextXMode){ /* pitch any LF's, Nulls and control Z's */
  721.         for(i=0;i<sectorSize;i++)
  722.          if((buff[i+2]!=LF) && (buff[i+2]!=0)
  723.              && (buff[i+2]!= CTLZ))OutCh(logFile,buff[i+2]);
  724.         }
  725.      else /* plain output */
  726.      OutBlock(logFile,&buff[2],sectorSize);
  727.      NumToString(total,temp);
  728.      SetIText(blockNumH,temp);
  729.      Message("Ok");
  730.      
  731.      blockNumber++;    /* kick the block number */
  732.      total++;       /* and the total */
  733.      errCount = 0 ;    /* reset the error counter */
  734.      }  /* go get another block */
  735.    }    /* end of Xreceive */
  736.    
  737. Boolean eof(chan)    /* true if mark >= eof */
  738.   short chan;        /* refNum of open file */
  739.   { FCBPBRec pb;
  740.     OSErr err;
  741.     
  742.     memset(&pb,0,sizeof(FCBPBRec));    /* clear parameter block */
  743.     pb.ioRefNum = chan;
  744.     if(err = PBGetFCBInfo(&pb,false))
  745.       { ErrorMessage("Error in GetFCBInfo",err); return true;}
  746.     if(pb.ioFCBCrPs >= pb.ioFCBEOF) return true;
  747.     return false;
  748.    }
  749.  /********************************************************************/ 
  750. short ReadTextSector(chan,buff,blkno)
  751.   short chan;
  752.   char buff[];
  753.   long blkno;
  754.  { static char flag;    /* true -> last block ended with a CR */
  755.    char ch;
  756.    short i;
  757.    long cnt;
  758.    
  759.    if(blkno==1) flag = 0;    /* initialize flag */
  760.    i = 0;
  761.    
  762.    if(flag)buff[i++] = LF;    /* start block with LF */
  763.    flag = 0;            /* reset the flag */
  764.    
  765.    if(eof(chan) && (i==0)) return 0; /* finished ! */
  766.    
  767.    while(i < sectorSize)
  768.     if( eof(chan)) buff[i++] = 0;  /* fill remainder with nulls */
  769.     else {
  770.             cnt = 1;
  771.             FSRead(chan,&cnt,&ch); buff[i++] =ch;
  772.             if((ch== CR)&&(i==sectorSize))flag = 1;
  773.             else if(ch==CR) buff[i++]=LF;
  774.       }
  775.     return 1;   /* more to do */
  776.   }
  777.  /********************************************************************/  
  778.    
  779. short ReadSector(chan,buff)
  780.        short chan;
  781.        char buff[];
  782.       { short i;
  783.         long count;
  784.         OSErr err;
  785.         char temp[10];
  786.         long pos;
  787.         
  788.         count = sectorSize;
  789.            if(err = FSRead(chan,&count,buff))
  790.              if(err != eofErr) ErrorMessage("Read Reading File",err); 
  791.     
  792.     /*    NumToString(count,temp);Message(temp);
  793.     
  794.         GetFPos(chan,&pos);
  795.         NumToString(count,temp);Message(temp);
  796.     */    
  797.         if(count > 0){
  798.                if(count != sectorSize)
  799.                 for(i=count;i<sectorSize;i++) buff[i]=0; /* pad with nulls */
  800.             return 1 ;
  801.         }
  802.          else return 0;   /* eof */
  803.        }
  804.  /********************************************************************/ 
  805. short GetBlock(buff,blkno)
  806.   char buff[];        /* the array to fill */
  807.   long blkno ;        /* the current block number */
  808.   { static long dblk = -1;    /* total number of data blocks to send */
  809.     long rblks;        /* total number of resourced blocks to send */
  810.     FileParam fileBlock;/* for getfile info */
  811.     short err;        /* error catcher */
  812.     short type;
  813.     Handle item;
  814.     Rect box;
  815.     char temp[10];
  816.  
  817.     if (MacBinary && (blkno==1))
  818.      { memset(buff,0,sectorSize);
  819.        memset(&fileBlock,0,sizeof(FileParam));
  820.        
  821.        fileBlock.ioNamePtr = c2pstr(thefilename);
  822.        fileBlock.ioVRefNum = thefileRefNum;
  823.        
  824.        /* get the info about the file */
  825.        if(err = PBGetFInfo(&fileBlock,0))
  826.            ErrorMessage("Error getting file info,GetBlock",err);
  827.         /* restore the filename */
  828.         p2cstr(thefilename);
  829.        
  830.        /* set up the first block */
  831.        
  832.        /* collect the finder info */
  833.        memcpy(&buff[65],&fileBlock.ioFlFndrInfo,16);
  834.        buff[74] = (char) 0;  /* keep this location clear */
  835.        
  836.        /* the creation and modification dates */
  837.        memcpy(&buff[91],&fileBlock.ioFlCrDat,8);
  838.        
  839.        /* move the file name */
  840.        buff[1] = strlen(thefilename);
  841.        memcpy(&buff[2],thefilename,buff[1]);
  842.        
  843.        memcpy(&buff[83],&fileBlock.ioFlLgLen,4); /* data size */
  844.        memcpy(&buff[87],&fileBlock.ioFlRLgLen,4);/* resource size */
  845.        
  846.        dblk = (fileBlock.ioFlLgLen+sectorSize-1)/sectorSize;
  847.        rblks= (fileBlock.ioFlRLgLen+sectorSize-1)/sectorSize;
  848.        gTotal = 1+dblk+rblks;
  849.        
  850.        GetDItem(dlog,TOTAL_ITEM,&type,&item,&box);
  851.        NumToString(gTotal,temp);
  852.        SetIText(item,temp);
  853.        
  854.        /* I don't know how to find the Protect Bit */
  855.        
  856.        if(!dblk) /* no data fork */
  857.          { FSClose(thefile); thefile = 0;
  858.            if(err=OpenRF(thefilename,thefileRefNum,&thefile))
  859.              Message("Error Opening Resource Fork");
  860.             if(err=SetFPos(thefile,fsFromStart,0))
  861.              Message("error setting fpos");
  862.           }
  863.        
  864.        /* more to do */
  865.        return 1;
  866.      }
  867.      
  868.    if (MacBinary && (blkno == 1+dblk)) /* last data block */
  869.         {  ReadSector(thefile,buff);  /* get a block */
  870.                FSClose(thefile);            /* close data fork */
  871.             thefile = 0;
  872.             if(err=OpenRF(thefilename,thefileRefNum,&thefile))
  873.               Message("Error Opening Resource fork");
  874.             if(err=SetFPos(thefile,fsFromStart,0))
  875.               Message("Error Setting FPos");
  876.                return 1;  /* eof on resource fork will stop everything */
  877.          }
  878.  
  879.     /* if we get here we have a run of the mill block */
  880.     if(TextXMode) return ReadTextSector(thefile,buff,blkno);
  881.     else return ReadSector(thefile,buff);
  882.     
  883.   }
  884.  
  885.  /********************************************************************/  
  886.  
  887. char waitStart(chan)
  888.      short chan;
  889.      { char c;
  890.        short inTime;
  891.        short nakWait;
  892.        
  893.        nakWait = 0;
  894.        do
  895.         {inTime = TimedRead(chan,&c,1,NAKLIM); /* wait NAKLIM seconds */
  896.      if(!inTime)
  897.       {if (++nakWait >= 10) Cancel(chan,1); /* this won't come back */
  898.        continue;  /* try for NAK again */
  899.        }
  900.      else
  901.       switch(c)
  902.        { case CAN: Cancel(chan,2); break;
  903.          case NAK: return 0;  /* let's go! */
  904.          case CRCCHAR:if(!forceCheckSum) return 1; /* note fall through */
  905.          default:  if( nakWait++ >=10) Cancel(chan,3); continue;
  906.         }
  907.      }
  908.     while(true);
  909.        }
  910.  /********************************************************************/  
  911.        
  912. short GetAck(chan,err)
  913.        short chan;
  914.        short *err;
  915.        { char c;
  916.          short inTime;
  917.          long cnt;
  918.          
  919.      /* flush input buffer */
  920.      while (ChAvail(chan)){cnt = 1; FSRead(chan,&cnt,&c);}
  921.      
  922.      inTime = TimedRead(chan,&c,1,ACKLIMTIM);
  923.      
  924.      if( inTime && (c == ACK)) return 1;  /* success */
  925.      else
  926.       { if( (*err)++>=ERRLIM) Cancel(chan,4); /* too many errors for this sector */
  927.         return 0;  /* failure */
  928.        }
  929.     }
  930.  /********************************************************************/  
  931.  
  932. void Xsend()
  933.   { short error;      /* from signal */
  934.     short moreToSend;    /* true->file not exhausted */
  935.     char  buff[1024+3];    /* next chunk of file Plus block header */
  936.     char  blockNumber;
  937.     char  checkSum;
  938.     short errorCount;    /* accumulator for sector errors */
  939.     short i;
  940.     char  crcMode;
  941.     long total;        /* current block number */
  942.     EventRecord event;  /* gets mousedown event to cancel */
  943.     short type;
  944.     Handle item;
  945.     Rect box;
  946.     static WindowPtr old ;    /* previous port */
  947.     char temp[10] ;    /* conversion storage */
  948.     Handle blockNumH;
  949.     IOParam rBlock ;    /* for asynch write */
  950.     IOParam ckBlock;    /* for sending checksum/crc */
  951.     static long stime,ftime;   /* start and finish times (ticks) */
  952.     unsigned short theCRC;
  953.     
  954.     dlog = (DialogPtr) 0;
  955.     
  956.        
  957.     if(!Sendfile()){ 
  958.          OutCh(sOut,CAN);return; } /* user changed mind */
  959.     
  960.            
  961.     /* put up the dialog box */
  962.      GetPort(&old);   /* save old port */
  963.      HideWindow(myWindow);    /* hide the tty window */
  964.      dlog = GetNewDialog(PROGRESS_ID ,0,(WindowPtr)-1); /* create the dialogue */
  965.      
  966.      DrawDialog(dlog);
  967.      
  968.      /* get a handle to the status field for ease of access */
  969.      
  970.      GetDItem(dlog,STATUS_ITEM,&type,&statusHandle,&box);
  971.      GetDItem(dlog,BLOCK_ITEM,&type,&blockNumH,&box);
  972.      
  973.      /* set some fields */
  974.      GetDItem(dlog,TRANS_ITEM,&type,&item,&box);
  975.      SetIText(item,"Sending");
  976.      
  977.      GetDItem(dlog,PROTOCOL_ITEM,&type,&item,&box);
  978.      if(MacBinary)SetIText(item,"MacBinary");
  979.      else if(TextXMode) SetIText(item,"Text Xmodem");
  980.           else SetIText(item,"Xmodem");
  981.       
  982.      GetDItem(dlog,FILE_ITEM,&type,&item,&box);
  983.      SetIText(item,thefilename);
  984.      
  985.      GetDItem(dlog,OF_ITEM,&type,&item,&box);
  986.      if (TextXMode) SetIText(item,"Of About");
  987.      else SetIText(item,"Of");
  988.      
  989.      GetDItem(dlog,TOTAL_ITEM,&type,&item,&box);
  990.      NumToString(gTotal,temp);
  991.      SetIText(item,temp);
  992.      Message("Waiting For Handshake");
  993.      
  994.     /* establish an error handler */
  995.     if(error = setjmp(env))
  996.       {if(dlog){
  997.          DisposDialog(dlog);ShowWindow(myWindow);SetPort(old);}
  998.        switch(error)
  999.         { case 1:drawCString("\r*** Host not transmitting. ***"); break;
  1000.       case 2:drawCString("\r*** Host sent initial CAN. ***"); break;
  1001.       case 3:drawCString("\r*** Host not transmitting NAK or CAN ***");break;
  1002.       case 4:drawCString("\r*** Too many errors for block ");
  1003.                NumToString(total,temp);
  1004.              drawCString(temp);
  1005.                 drawCString(" ***");
  1006.              break;
  1007.       case 5:drawCString("\r*** User aborted send. ***");
  1008.              OutCh(sOut,CAN);
  1009.              break;
  1010.       case 6:drawCString("\r*** File System Error ***");
  1011.              OutCh(sOut,CAN);
  1012.              break;
  1013.       default:drawCString("\r*** Undefined error. ***"); break;
  1014.      }
  1015.     FSClose(thefile); thefile = 0;
  1016.     SysBeep(7);SysBeep(7);
  1017.     return;
  1018.        }   /* end of error handler */
  1019.     
  1020.     crcMode = waitStart(sIn);   /* wait for NAK or C */
  1021.     
  1022.     GetDItem(dlog,ERROR_ITEM,&type,&item,&box);
  1023.     if (crcMode)SetIText(item,"CCITT-CRC With ARQ");
  1024.     else SetIText(item,"Eight Bit CheckSum");
  1025.       
  1026.     blockNumber = 1;total = 1; stime = TickCount();
  1027.     do
  1028.      { if(GetBlock(&buff[3],total))
  1029.         { errorCount = 0;
  1030.       do { 
  1031.                       
  1032.            buff[0] = SOH;
  1033.            buff[1] = blockNumber;
  1034.            buff[2] = ~blockNumber;
  1035.            
  1036.            /* set up for  write */
  1037.            rBlock.ioCompletion = 0 ;    /* no completion routine */
  1038.            rBlock.ioVRefNum = 0;
  1039.            rBlock.ioRefNum = sOut;     /* select read port */
  1040.            rBlock.ioBuffer = buff;        /* where to write from */
  1041.            rBlock.ioReqCount = sectorSize+3;    /* byte count to write */
  1042.            rBlock.ioPosMode = 0;
  1043.    
  1044.            PBWrite(&rBlock,1);     /* post asynch write */
  1045.            
  1046.         
  1047.            /* while the block is being sent do other stuff */
  1048.            
  1049.            if(GetNextEvent(mDownMask,&event))
  1050.              { /* need to kill io here */
  1051.                 longjmp(env,5);
  1052.               }
  1053.               
  1054.                if(showClock)
  1055.              if (GetNextEvent(app2Mask,&event))showTime(); /* adjust clock */
  1056.  
  1057.            
  1058.            NumToString(total,temp);
  1059.            SetIText(blockNumH,temp);
  1060.            
  1061.            if(errorCount)Message("Retransmitting Block");
  1062.            else Message("");
  1063.            
  1064.            if (crcMode)
  1065.               theCRC = table_driven_crc(0,&buff[3],sectorSize,crc_table);
  1066.             else
  1067.             checkSum = CheckSum(&buff[3],sectorSize);
  1068.            
  1069.            
  1070.  
  1071.            /* set up for  write of checksum/crc */
  1072.            ckBlock.ioCompletion = 0 ;    /* no completion routine */
  1073.            ckBlock.ioVRefNum = 0;
  1074.            ckBlock.ioRefNum = sOut;     /* select read port */
  1075.            ckBlock.ioBuffer = (crcMode)?(char *) &theCRC :(char *) &checkSum;    /* where to write from */
  1076.            ckBlock.ioReqCount = (crcMode)?2:1;    /* byte count to write */
  1077.            ckBlock.ioPosMode = 0;
  1078.         
  1079.         PBWrite(&ckBlock,0); /* when this completes we are done */
  1080.  
  1081.         
  1082.           } /* check for an ACK */
  1083.        while(!GetAck(sIn,&errorCount)) ; /* retransmit block if no ACK */
  1084.        blockNumber++;  /* set up for next block */
  1085.        total++ ;
  1086.       }
  1087.     else     /* file transmission is complete notify receiver and quit */
  1088.      { errorCount = 0;ftime = TickCount();
  1089.        do OutCh(sOut,EOT) ; while(!GetAck(sIn,&errorCount));
  1090.        FSClose(thefile);thefile=0;
  1091.        DisposDialog(dlog);ShowWindow(myWindow);SetPort(old);
  1092.        drawCString("\r\012***Transmission Complete***\r\012");
  1093.        NumToString(sectorSize*(total-1),temp);
  1094.        drawCString(temp);
  1095.        drawCString(" user bytes sent.");
  1096.        drawCString(" Effective Transmission Rate =");
  1097.        NumToString((sectorSize*(total-1)*60)/(ftime-stime),temp);
  1098.        drawCString(temp);
  1099.        drawCString(" bytes/second\r\012");
  1100.        SysBeep(7);SysBeep(7);
  1101.        return ; /* to the main program */
  1102.       }
  1103.      }
  1104.     while (true);
  1105.    }
  1106.